package gameAppLayer.stateMachine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.scene.Node;

import utilities.Key;

import model.AbstractModel;

import controller.AbstractController;
import controller.IController;
import controls.IMasterControl;
import de.lessvoid.nifty.screen.ScreenController;
import eventManager.EventManager;
import eventManager.EventPortal;

/*
 * A particular GameState
 */
public abstract class GameState implements AnalogListener, ActionListener {
	
	private static final Logger logger = Logger.getLogger(GameState.class.getName());
	
	protected String stateID;
	protected InputManager inputManager;
	private enum InnerState {
		RUN,
		RUN_ALTERNATE,
		HALT;
	}	
	private InnerState currState;
	private boolean isClear;
	
	public ArrayList<AbstractScreenController> screenControllers = new ArrayList<AbstractScreenController>();
	public ArrayList<IMasterControl> masterControls;
	public HashMap<Key, IMasterControl> masterControlMap;// = new HashMap<Key, IMasterControl>();
	
	public GameState(String stateID, InputManager inputManager) {
		this.stateID = stateID;
		currState = InnerState.HALT;
		isClear = true;
		logger.log(Level.INFO, "GameState: GameState() " + stateID + " created (NOT started yet)");
	}
	
	public String getGameStateID() {
		return stateID;
	}
	
	/**
	 * Set this gameState to RUN. Added to update() loop.
	 */
	public void run() {
		currState = InnerState.RUN;
		onRun();
	}
	
	/**
	 * Set this gameState to RUN_ALTERNATE. A secondary mode that can define
	 * alternative state behavior when paused, etc. State is attached to the
	 * update() loop. Behaviors under RUN are NOT executed by update().
	 */
	public void runAlternate() {
		currState = InnerState.RUN_ALTERNATE;
		onRunAlternate();
	}
	
	/**
	 * Set this gameState to HALT. GameState is removed from update() loop,
	 * removed from display tree, eventManager, etc.  Goes inactive, but 
	 * state data is retained.
	 */
	public void halt() {
		currState = InnerState.HALT;
		onHalt();//deleteListeners();
	}
	
	/**
	 * Deletes all data in this gameState. First sets state to HALT, then
	 * deletes data. Must use initialize() to rebuild the state to use again.
	 */
	public boolean clear(GameStateMachine stateMachine) {
		this.halt();
		stateMachine.deactivateState(this); //QQQ: May need to turn this into a string?
		boolean checkDestroy = destroyState();
		
		if (checkDestroy) {
			isClear = true;
		}
		else 
			isClear = false;
		
		return isClear;
	}	
	
	/**
	 * Contains detailed instructions for deletion of state data
	 * @return
	 */
	protected abstract boolean destroyState();
	
	/**
	 * Initializes all gameState data from scratch.  State must be empty or
	 * previously clear() to successfully initialize()
	 */
	public boolean initialize(GameStateMachine stateMachine) {
		if (isClear) {
			boolean checkBuild = this.buildState();
			
			boolean checkRegister = false;
			
			if (checkBuild) 
				checkRegister = registerListeners();
			
			return checkRegister;
		}
		else {
			return false; //state not cleared, this method won't clear on its own as may be too costly.
		}
	}
	
	/**
	 * Contains detailed instructions for creation of state data
	 * @return
	 */
	protected abstract boolean buildState();
	
	/**
	 * Register all of this GameState's listeners with EventManager
	 * @return
	 */
	protected boolean registerListeners() {
		logger.log(Level.INFO, "GameState: registerListeners(); Number of controllers= " + masterControls.size());
		int i = 0;
		for (IMasterControl control: masterControls) {
			control.registerEvents();			
		}
		logger.log(Level.INFO, "GameState: registerListeners(); Total non-unique events registered= " + i);
		
		EventPortal.addControllerMapReference(this.masterControlMap);
				
		logger.log(Level.INFO, "GameState: registerListeners() " + stateID + " COMPLETE");
		
		return true;
	}
	
	/**
	 * Remove all of this GameState's listeners from EventManager
	 * @return
	 */
	protected boolean deleteListeners() {
		for (IMasterControl control: masterControls) {
			control.deleteEvents();	
		}	
		
		logger.log(Level.INFO, "deleteListeners() from " + stateID);
		
		return true;
	}
	
	public void update(float tpf) {
		if (!currState.equals(InnerState.HALT)) {
			for (AbstractScreenController screen: screenControllers) {
				screen.update(tpf);
			}
			
			for (IMasterControl controller: masterControls) {
				controller.update(tpf);
			}
		}
	}
	
	/**
	 * Define state behavior when paused or otherwise disabled.
	 */
	public abstract void onRun();
	public abstract void onRunAlternate();
	public abstract void onHalt();
	
}